package com.ecreditpal.audio.util;

import lombok.extern.slf4j.Slf4j;
import org.apache.http.*;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.EntityBuilder;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.config.SocketConfig;
import org.apache.http.conn.DnsResolver;
import org.apache.http.conn.HttpConnectionFactory;
import org.apache.http.conn.ManagedHttpClientConnection;
import org.apache.http.conn.routing.HttpRoute;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.entity.ByteArrayEntity;
import org.apache.http.entity.FileEntity;
import org.apache.http.entity.StringEntity;
import org.apache.http.entity.mime.MultipartEntityBuilder;
import org.apache.http.impl.DefaultConnectionReuseStrategy;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.DefaultConnectionKeepAliveStrategy;
import org.apache.http.impl.client.DefaultHttpRequestRetryHandler;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.DefaultHttpResponseParserFactory;
import org.apache.http.impl.conn.ManagedHttpClientConnectionFactory;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.impl.conn.SystemDefaultDnsResolver;
import org.apache.http.impl.io.DefaultHttpRequestWriterFactory;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;

import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;


/**
 * @author lifeng
 * @version 1.0 on 2017/6/19.
 */

@Slf4j
public class MyHttpClient {
    private static CloseableHttpClient httpClient = null;

    static {
        if (httpClient == null) {
            synchronized (MyHttpClient.class) {
                if (httpClient == null) {
                    httpClient = getHttpClient();
                }
            }
        }
    }

    private MyHttpClient() {
    }

    public static CloseableHttpClient getHttpClient() {
        //注册访问协议相关的socket工厂
        Registry<ConnectionSocketFactory> socketFactoryRegistry =
                RegistryBuilder.<ConnectionSocketFactory>create()
                        .register("http", PlainConnectionSocketFactory.INSTANCE)
                        .register("https", SSLConnectionSocketFactory.getSystemSocketFactory()).build();
        //HttpConnection工厂:配置写请求/解析相应处理器
        HttpConnectionFactory<HttpRoute, ManagedHttpClientConnection> connectionFactory =
                new ManagedHttpClientConnectionFactory(DefaultHttpRequestWriterFactory.INSTANCE,
                        DefaultHttpResponseParserFactory.INSTANCE);
        //DNS解析器
        DnsResolver dnsResolver = SystemDefaultDnsResolver.INSTANCE;
        //创建池化链接管理器
        PoolingHttpClientConnectionManager manager = new PoolingHttpClientConnectionManager(socketFactoryRegistry, connectionFactory, dnsResolver);
        //默认为Socket配置
        SocketConfig defaultSocketConfig = SocketConfig.custom().setTcpNoDelay(true).build();
        manager.setDefaultSocketConfig(defaultSocketConfig);
        //设置连接池的最大连接数
        manager.setMaxTotal(64);
        //每个路由的默认最大链接,每个路由实际最大连接数默认为DefaultMaxPerRoute控制,
        //而MaxTotal是控制整个池子的最大数
        //设置过小无法支持大并发(ConnectionPoolTimeoutException: Timeout waiting for connection from pool)
        //路由是对maxTotal的细分
        manager.setDefaultMaxPerRoute(32);
        //在连接池获取连接时,链接不活跃多长时间后需要进行一次验证,默认为2s
        manager.setValidateAfterInactivity(5 * 1000);

//        String target = ConfigurationManager.getConfiguration().getString("maas.headstream", "dolphin.myecreditpal.com");
//        int port = ConfigurationManager.getConfiguration().getInt("maas.headstream.port", 8888);
//        manager.setMaxPerRoute(new HttpRoute(new HttpHost(target, port)), 64);

        //默认请求配置
        RequestConfig defaultRequestConfig = RequestConfig.custom()
                .setConnectTimeout(10 * 1000) // 设置链接超时时间
                .setSocketTimeout(10 * 1000)//设置等待数据超时时间 //HOT TRY BY Xiangping, adjust required
                .setConnectionRequestTimeout(10000)
                //设置从连接池获取连接的等待超时时间
                .build();

        //创建HttpClient
        httpClient = HttpClients.custom()
                .setConnectionManager(manager)
                .setConnectionManagerShared(false) //非共享模式连接池
                .evictIdleConnections(600, TimeUnit.SECONDS)//定期回收空闲链接
                .evictExpiredConnections()//定期回收过期链接a
                //链接存活时间,如果不设置,则根据长连接信息决定
                .setConnectionTimeToLive(60, TimeUnit.SECONDS)
                //设置默认请求配置
                .setDefaultRequestConfig(defaultRequestConfig)
                //链接重用策略,即是否能keepAlive
                .setConnectionReuseStrategy(DefaultConnectionReuseStrategy.INSTANCE)
                //长连接配置,即获取长连接生产多长时间
                .setKeepAliveStrategy(DefaultConnectionKeepAliveStrategy.INSTANCE)
                //设置重试次数,默认是3次:当前是禁用状态
                .setRetryHandler(new DefaultHttpRequestRetryHandler(0, false))
                .build();
        Runtime.getRuntime().addShutdownHook(new Thread() {
            @Override
            public void run() {
                try {
                    httpClient.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        });

        return httpClient;
    }

    public static String get(String url) {
        HttpResponse response = null;
        HttpGet get = new HttpGet(url);
        try {
            response = httpClient.execute(get);
            if (response.getStatusLine().getStatusCode() != HttpStatus.SC_OK) {
                EntityUtils.consume(response.getEntity());
            } else {
                return EntityUtils.toString(response.getEntity(), Charset.forName("utf-8"));
            }
        } catch (Exception e) {
            log.error("error sending get request", e);
            if (response != null) {
                try {
                    EntityUtils.consume(response.getEntity());
                } catch (IOException e1) {
                    log.error("error getting the entity", e);
                }
            }
        }
        return null;
    }

    public static String post(String url, Map<String, String> params) {
        HttpResponse response = null;
        HttpPost post = new HttpPost(url);
        List<NameValuePair> nameValuePair = new ArrayList<>(params.size());
        //lamba 表达式构造http请求所需的参数
        params.forEach((k, v) -> nameValuePair.add(new BasicNameValuePair(k, v)));
        log.debug("http request url:{} , content:{}", url, nameValuePair.toString());
        try {
            post.setEntity(new UrlEncodedFormEntity(nameValuePair, "utf-8"));
            response = httpClient.execute(post);
            if (response.getStatusLine().getStatusCode() != HttpStatus.SC_OK) {
                EntityUtils.consume(response.getEntity());
            } else {
//                String charset = getContentCharSet(response.getEntity());
                return EntityUtils.toString(response.getEntity(), "utf-8");
            }
        } catch (Exception e) {
            log.error("error sending post request the request", e);
            if (response != null) {
                try {
                    EntityUtils.consume(response.getEntity());
                } catch (IOException e1) {
                    log.error("error getting the entity", e);
                }
            }
        }
        return String.valueOf(response.getStatusLine().getStatusCode());
    }

    public static String postJson(String url, String json) {
        HttpResponse response = null;
        HttpPost post = new HttpPost(url);

        try {
            post.setEntity(new StringEntity(json, "utf-8"));
            response = httpClient.execute(post);
            if (response.getStatusLine().getStatusCode() != HttpStatus.SC_OK) {
                EntityUtils.consume(response.getEntity());
            } else {
//                String charset = getContentCharSet(response.getEntity());
                return EntityUtils.toString(response.getEntity(), "utf-8");
            }
        } catch (Exception e) {
            log.error("error sending post request the request", e);
            if (response != null) {
                try {
                    EntityUtils.consume(response.getEntity());
                } catch (IOException e1) {
                    log.error("error getting the entity", e);
                }
            }
        }
        return String.valueOf(response.getStatusLine().getStatusCode());
    }


    public static String postFile(String url, Map<String, String> headers, byte[] data) {
        HttpResponse response = null;
        HttpPost post = new HttpPost(url);
        for (String s : headers.keySet()) {
            post.addHeader(s, headers.get(s));
        }

        try {
            ByteArrayEntity entity = new ByteArrayEntity(data);

            post.setEntity(entity);
            response = httpClient.execute(post);
            if (response.getStatusLine().getStatusCode() != HttpStatus.SC_OK) {
                EntityUtils.consume(response.getEntity());
            } else {
                return EntityUtils.toString(response.getEntity(), "utf-8");
            }
        } catch (Exception e) {
            log.error("error sending post request the request", e);
            if (response != null) {
                try {
                    EntityUtils.consume(response.getEntity());
                } catch (IOException e1) {
                    log.error("error getting the entity", e);
                }
            }
        }
        return String.valueOf(response.getStatusLine().getStatusCode());
    }


    public static String postWithGzip(String url, Map<String, String> params) {
        HttpResponse response = null;
        HttpPost post = new HttpPost(url);
        List<NameValuePair> nameValuePair = new ArrayList<>(params.size());
        //lamba 表达式构造http请求所需的参数
        params.forEach((k, v) -> nameValuePair.add(new BasicNameValuePair(k, v)));
        log.debug("http request url:{} , content:{}", url, nameValuePair.toString());

        try {
            HttpEntity entity = EntityBuilder.create()
                    .setStream(new UrlEncodedFormEntity(nameValuePair, "utf-8").getContent())
                    .gzipCompress()
                    .build();
            post.setEntity(entity);
            post.setHeader("Content-Type", "application/x-www-form-urlencoded");
            response = httpClient.execute(post);
            if (response.getStatusLine().getStatusCode() != HttpStatus.SC_OK) {
                EntityUtils.consume(response.getEntity());
            } else {
                return EntityUtils.toString(response.getEntity(), "utf-8");
            }
        } catch (Exception e) {
            log.error("error sending post request the request", e);
            if (response != null) {
                try {
                    EntityUtils.consume(response.getEntity());
                } catch (IOException e1) {
                    log.error("error getting the entity", e);
                }
            }
        }
        return null;
    }


}
